﻿using System;
using SoftwareConsulting.BI11.NumericalMethods.Curves;
using SoftwareConsulting.BI11.NumericalMethods.Statistics;
using SoftwareConsulting.BI11.Studio.InternalServices;

namespace SoftwareConsulting.BI11.NumericalMethods.Statistics
{
    /// <summary>
    /// Класс экспоненциального распределения
    /// </summary>
    public sealed class ExponentialDistribution : ProbabilityDensityFunction
    {	    
        private double _betaFallOff;//коэффициент спада

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="betaFallOff">коэффициент спада</param>
        public ExponentialDistribution(double betaFallOff)
        {
            if (betaFallOff <= 0)
                throw new ArgumentOutOfRangeException("betaFallOff");
            _betaFallOff = betaFallOff;
        }

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="histogram">Гистограмма, из которой вытягиваются параметры по наилучшим предположениям</param>
        public ExponentialDistribution(Histogram histogram)
        {
            if (histogram.Minimum < 0)
                throw new ArgumentOutOfRangeException();
            double average = histogram.Average;
            if (average < 0)
                throw new ArgumentOutOfRangeException();
            BetaFallOff = average;
        }

        /// <summary>
        /// Возвращает среднее распределения
        /// </summary>
        public override double Average
        {
            get 
            { 
                return _betaFallOff; 
            }
            set
            {
                Assert.Fail(Assert.Id.Assert00278);//для экспоненциального распределения недоступно
            }
        }

        /// <summary>
        /// Возвращает значение функции плотности распределения
        /// </summary>
        /// <param name="x"></param>
        /// <returns>вероятность, нахождения случайной переменной меньше или равной, чем X</returns>
        public override double GetDistributionValue(double x)
        {
            return 1 - Math.Exp(-x / _betaFallOff);
        }

        /// <summary>
        /// Возвращает эксцесс распределения
        /// </summary>
        public override double Kurtosis
        {
            get 
            { 
                return 6; 
            }
        }

        /// <summary>
        /// Возвращает название распределения
        /// </summary>
        public override string Name
        {
            get 
            { 
                return "Exponential distribution"; 
            }
        }

        /// <summary>
        /// Возвращает/устанавливает параметры распределения
        /// </summary>
        public override double[] Parameters
        {
            get 
            { 
                return new double[] { _betaFallOff }; 
            }
            set 
            { 
                BetaFallOff = value[0]; 
            }
        }

        /// <summary>
        /// Возвращает значение, для которого функция распределения равна указанному значению X
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        protected override double GetInversedValue(double x)
        {
            return -Math.Log(1 - x) * _betaFallOff;
        }

        /// <summary>
        /// Возвращает случайное вещественное число в соответствие с распределением
        /// </summary>
        /// <returns></returns>
        public override double GetRandomValue()
        {            
            return -_betaFallOff * Math.Log(this.RandomGenerator.NextDouble());
        }

        /// <summary>
        /// Возвращает/устанавливает коэффициент спада
        /// </summary>
        public double BetaFallOff
        {
            get
            {
                return _betaFallOff;
            }
            set 
            { 
                _betaFallOff = value; 
            }
        }

        /// <summary>
        /// Возвращает коэффициент уклона распределения
        /// </summary>
        public override double Skewness
        {
            get 
            {                         
                return 2; 
            }
        }

        /// <summary>
        /// Возвращает стандартное отклонение дисперсии распределения
        /// </summary>
        public override double StandardDeviation
        {
            get 
            {                 
                return _betaFallOff; 
            }
        }

        /// <summary>
        /// Возвращает как строку
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return string.Format("{0} ({1:###0.00000})", Name, _betaFallOff);
        }

        /// <summary>
        /// Вычисляет значение, основываясь на функции плотности
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        public override double GetValue(double x)
        {
            return Math.Exp(-x / _betaFallOff) / _betaFallOff;
        }

        /// <summary>
        /// Возвращает значение распределения и градиент распределения с учетом параметров
        /// </summary>
        /// <param name="x"></param>
        /// <returns>0й элемент - значение распределения, 1й и последующие - градиент распределения</returns>
        public override double[] GetValueAndGradient(double x)
        {
            double[] result = new double[2];
            result[0] = GetValue(x);
            result[1] = (x / _betaFallOff - 1) * result[0] / _betaFallOff;
            return result;
        }
    }
}
